package com.downbox.service.impl;

import com.downbox.config.GlobalExceptionHandler.BusinessException;
import com.downbox.domain.Content;
import com.downbox.domain.DrawHistory;
import com.downbox.domain.QuotaDaily;
import com.downbox.dto.DrawResp;
import com.downbox.dto.HistoryItemDTO;
import com.downbox.dto.PageResp;
import com.downbox.repository.ContentRepository;
import com.downbox.repository.DrawHistoryRepository;
import com.downbox.repository.QuotaDailyRepository;
import com.downbox.service.ContentService;
import com.downbox.service.MoodService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 内容服务实现类
 */
@Service
public class ContentServiceImpl implements ContentService {

    @Autowired
    private ContentRepository contentRepository;

    @Autowired
    private DrawHistoryRepository drawHistoryRepository;

    @Autowired
    private QuotaDailyRepository quotaDailyRepository;

    @Autowired
    private MoodService moodService;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    @Transactional
    public DrawResp drawContent(Long userId, String type) {
        LocalDate today = LocalDate.now();
        QuotaDaily quotaDaily = quotaDailyRepository.findByUidAndDate(userId, today)
                .orElseGet(() -> {
                    QuotaDaily newQuota = new QuotaDaily();
                    newQuota.setUid(userId);
                    newQuota.setDate(today);
                    newQuota.setDrawUsed(false);
                    newQuota.setBonusLeft(0);
                    return newQuota;
                });

        boolean fromBonus = false;
        // 先检查是否可以抽奖，但不修改次数
        if (quotaDaily.getDrawUsed()) {
            // 检查是否有奖励次数
            if (quotaDaily.getBonusLeft() <= 0) {
                throw new BusinessException(403, "今日抽奖次数已用完");
            }
            fromBonus = true;
        }

        // 获取用户今日情绪
        List<String> userMoods = moodService.getUserTodayMoods(userId);

        // 进行加权随机抽奖
        Content content = weightedRandomContent(type);
        if (content == null) {
            throw new BusinessException(404, "暂无可用内容");
        }
        
        // 只有在成功获取内容后才减少抽奖次数
        if (fromBonus) {
            quotaDaily.setBonusLeft(quotaDaily.getBonusLeft() - 1);
        } else {
            quotaDaily.setDrawUsed(true);
        }

        // 记录抽奖历史
        DrawHistory history = new DrawHistory();
        history.setUid(userId);
        history.setContentId(content.getId());
        history.setType(content.getType().name());
        history.setFromBonus(fromBonus);
        try {
            history.setMoodsJson(objectMapper.writeValueAsString(userMoods));
        } catch (JsonProcessingException e) {
            history.setMoodsJson("[]");
        }
        drawHistoryRepository.save(history);
        quotaDailyRepository.save(quotaDaily);

        // 构建响应
        DrawResp resp = new DrawResp();
        resp.setHistoryId(history.getId());
        resp.setHasDrawnToday(quotaDaily.getDrawUsed());
        resp.setBonusLeft(quotaDaily.getBonusLeft());

        try {
            DrawResp.ContentDTO contentDTO = new DrawResp.ContentDTO();
            contentDTO.setId(content.getId());
            contentDTO.setType(content.getType().name());
            // 将payload转换为Map<String, Object>类型
            contentDTO.setPayload(objectMapper.readValue(content.getPayload(), new TypeReference<Map<String, Object>>() {}));
            contentDTO.setTags(objectMapper.readValue(content.getTagsJson(), new TypeReference<List<String>>() {}));
            resp.setContent(contentDTO);
        } catch (JsonProcessingException e) {
            throw new BusinessException(500, "解析内容失败");
        }

        return resp;
    }

    @Override
    public boolean hasDrawnToday(Long userId) {
        LocalDate today = LocalDate.now();
        return quotaDailyRepository.findByUidAndDate(userId, today)
                .map(QuotaDaily::getDrawUsed)
                .orElse(false);
    }

    @Override
    public PageResp<HistoryItemDTO> getDrawHistory(Long userId, String type, int page, int size) {
        Pageable pageable = PageRequest.of(page - 1, size);
        Page<DrawHistory> historyPage;
        
        if (type != null && !type.isEmpty()) {
            historyPage = drawHistoryRepository.findByUidAndTypeOrderByCreatedAtDesc(userId, type, pageable);
        } else {
            historyPage = drawHistoryRepository.findByUidOrderByCreatedAtDesc(userId, pageable);
        }

        List<HistoryItemDTO> items = historyPage.getContent().stream().map(history -> {
            HistoryItemDTO item = new HistoryItemDTO();
            item.setHistoryId(history.getId());
            item.setType(history.getType());
            item.setCreatedAt(history.getCreatedAt());
            
            // 查询内容详情获取简介
            contentRepository.findById(history.getContentId()).ifPresent(content -> {
                try {
                // 从payload中提取简介
                Map<String, Object> payload = objectMapper.readValue(content.getPayload(), new TypeReference<Map<String, Object>>() {});
                if (payload.containsKey("text")) {
                    String text = (String) payload.get("text");
                    item.setContentBrief(text.length() > 30 ? text.substring(0, 30) + "..." : text);
                } else if (payload.containsKey("description")) {
                    item.setContentBrief((String) payload.get("description"));
                } else {
                    item.setContentBrief("内容摘要");
                }
                
                // 设置标签
                if (content.getTagsJson() != null) {
                    item.setTags(objectMapper.readValue(content.getTagsJson(), new TypeReference<List<String>>() {}));
                } else {
                    item.setTags(List.of());
                }
            } catch (JsonProcessingException e) {
                item.setContentBrief("内容摘要");
                item.setTags(List.of());
            }
            });
            
            return item;
        }).collect(Collectors.toList());

        return new PageResp<>(page, size, historyPage.getTotalElements(), items);
    }

    @Override
    @Transactional
    public void receiveBonus(Long userId) {
        LocalDate today = LocalDate.now();
        QuotaDaily quotaDaily = quotaDailyRepository.findByUidAndDate(userId, today)
                .orElseGet(() -> {
                    QuotaDaily newQuota = new QuotaDaily();
                    newQuota.setUid(userId);
                    newQuota.setDate(today);
                    newQuota.setDrawUsed(false);
                    newQuota.setBonusLeft(0);
                    return newQuota;
                });

        // 增加奖励次数（这里简化处理，每次领取增加1次）
        quotaDaily.setBonusLeft(quotaDaily.getBonusLeft() + 1);
        quotaDailyRepository.save(quotaDaily);
    }

    /**
     * 加权随机选择内容
     */
    private Content weightedRandomContent(String type) {
        List<Content> contents;
        if (type != null && !type.isEmpty()) {
            contents = contentRepository.findByType(Content.ContentType.valueOf(type));
        } else {
            contents = contentRepository.findAll();
        }

        if (contents.isEmpty()) {
            // 提前抛出异常，确保事务正确回滚
            throw new BusinessException(404, "暂无可用内容");
        }

        // 计算总权重
        int totalWeight = contents.stream()
                .mapToInt(content -> content.getWeight() != null ? content.getWeight() : 1)
                .sum();

        // 随机数
        int random = new Random().nextInt(totalWeight) + 1;
        int currentWeight = 0;

        // 按权重选择
        for (Content content : contents) {
            currentWeight += (content.getWeight() != null ? content.getWeight() : 1);
            if (random <= currentWeight) {
                return content;
            }
        }

        // 兜底返回第一个
        return contents.get(0);
    }
}